package com.specificgroup.jira.plugin.acpro.service.impl;

import com.specificgroup.jira.plugin.acpro.Constants;
import com.specificgroup.jira.plugin.acpro.exception.AcproSettingsXmlException;
import com.specificgroup.jira.plugin.acpro.model.Settings;
import com.specificgroup.jira.plugin.acpro.model.acpro.AcproLoginData;
import com.specificgroup.jira.plugin.acpro.service.JiraAcproSettingsService;
import com.specificgroup.jira.plugin.acpro.xml.settings.generated.*;
import org.apache.log4j.Logger;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.util.List;
import java.util.Map;

/**
 * @author Ivan Holub
 */
public class JiraAcproSettingsServiceImpl implements JiraAcproSettingsService {

    private Logger log = Logger.getLogger(getClass());

    public String save(Settings settings) {
        AcproLoginData acproLoginData = settings.getAcproLoginData();
        String acproProjectKey = settings.getAcproProjectKey();

        ObjectFactory factory = new ObjectFactory();
        JiraAcproSettingsType root = factory.createJiraAcproSettingsType();
        
        AcproLoginType acproLoginType = factory.createAcproLoginType();
        acproLoginType.setPostUrl(acproLoginData.getPostUrl());
        acproLoginType.setUserId(acproLoginData.getUsername());
        root.setAcproLogin(acproLoginType);

        AcproProjectType acproProjectType = factory.createAcproProjectType();
        acproProjectType.setKey(acproProjectKey);
        root.setAcproProject(acproProjectType);

        buildJiraProject(settings, root, factory);

        buildUserMapping(settings, root, factory);

        buildNotifyEmailMapping(settings, root, factory);

        buildIssueTypeMapping(settings, root, factory);

        buildPriorityMapping(settings, root, factory);

        buildStatusMapping(settings, root, factory);

        buildResolutionMapping(settings, root, factory);

        buildProjectVersionMapping(settings, root, factory);

        return marshal(root);
    }

    private void buildJiraProject(Settings settings, JiraAcproSettingsType root, ObjectFactory factory) {
        Long jiraProjectId = settings.getJiraProjectId();
        List<Long> jiraProjectComponentsIds = settings.getJiraProjectComponentsIds();
        JiraProjectType jiraProjectType = factory.createJiraProjectType();
        jiraProjectType.setId(jiraProjectId);
        if (jiraProjectComponentsIds != null && jiraProjectComponentsIds.size() > 0) {
            JiraProjectComponentsType jiraProjectComponentsType = factory.createJiraProjectComponentsType();
            List<JiraProjectComponentType> list = jiraProjectComponentsType.getJiraProjectComponent();
            for (Long id: jiraProjectComponentsIds) {
                JiraProjectComponentType jiraProjectComponentType = factory.createJiraProjectComponentType();
                jiraProjectComponentType.setId(id);
                list.add(jiraProjectComponentType);
            }
            jiraProjectType.setJiraProjectComponents(jiraProjectComponentsType);
        }
        root.setJiraProject(jiraProjectType);
    }

    private void buildUserMapping(Settings settings, JiraAcproSettingsType root, ObjectFactory factory) {
        Map<String, String> usersMapping = settings.getUsersMapping();
        UserMappingType userMappingType = factory.createUserMappingType();
        List<UserMappingEntryType> userMappingEntryList = userMappingType.getUserMappingEntry();
        for (String acproUserKey: usersMapping.keySet()) {
            String jiraUserName = usersMapping.get(acproUserKey);
            UserMappingEntryType entry = factory.createUserMappingEntryType();
            entry.setAcproUserKey(acproUserKey);
            entry.setJiraUserName(jiraUserName);
            userMappingEntryList.add(entry);
        }
        root.setUserMapping(userMappingType);
    }

    private void buildNotifyEmailMapping(Settings settings, JiraAcproSettingsType root, ObjectFactory factory) {
        Map<String, String> mapping = settings.getNotifyCCMapping();
        NotifyEmailMappingType type = factory.createNotifyEmailMappingType();
        List<NotifyEmailMappingEntryType> list = type.getNotifyEmailMappingEntry();
        for (String acproEmail: mapping.keySet()) {
            String jiraUserName = mapping.get(acproEmail);
            NotifyEmailMappingEntryType entry = factory.createNotifyEmailMappingEntryType();
            entry.setEmail(acproEmail);
            entry.setJiraUserName(jiraUserName);
            list.add(entry);
        }
        root.setNotifyEmailMapping(type);
    }

    private void buildIssueTypeMapping(Settings settings, JiraAcproSettingsType root, ObjectFactory factory) {
        Map<String, String> mapping = settings.getIssueTypeMapping();
        String categoryIsNotSetJiraIssueTypeId = settings.getCategoryIsNotSetJiraIssueTypeIdMapping();
        IssueTypeMappingType type = factory.createIssueTypeMappingType();
        List<IssueTypeMappingEntryType> list = type.getIssueTypeMappingEntry();
        for (String acproKey: mapping.keySet()) {
            String jiraId = mapping.get(acproKey);
            IssueTypeMappingEntryType entry = factory.createIssueTypeMappingEntryType();
            entry.setAcproCategoryKey(acproKey);
            entry.setJiraIssueTypeId(jiraId);
            list.add(entry);
        }
        if (categoryIsNotSetJiraIssueTypeId != null) {
            CategoryIsNotSetType c = factory.createCategoryIsNotSetType();
            c.setJiraIssueTypeId(categoryIsNotSetJiraIssueTypeId);
            type.setCategoryIsNotSet(c);
        }
        root.setIssueTypeMapping(type);
    }

    private void buildPriorityMapping(Settings settings, JiraAcproSettingsType root, ObjectFactory factory) {
        Map<String, String> mapping = settings.getPriorityMapping();
        PriorityMappingType type = factory.createPriorityMappingType();
        List<PriorityMappingEntryType> list = type.getPriorityMappingEntry();
        for (String acproKey: mapping.keySet()) {
            String jiraId = mapping.get(acproKey);
            PriorityMappingEntryType entry = factory.createPriorityMappingEntryType();
            entry.setAcproUrgencyKey(acproKey);
            entry.setJiraPriorityId(jiraId);
            list.add(entry);
        }
        root.setPriorityMapping(type);
    }

    private void buildStatusMapping(Settings settings, JiraAcproSettingsType root, ObjectFactory factory) {
        Map<String, String> mapping = settings.getStatusMapping();
        StatusMappingType type = factory.createStatusMappingType();
        List<StatusMappingEntryType> list = type.getStatusMappingEntry();
        for (String acproKey: mapping.keySet()) {
            String jiraId = mapping.get(acproKey);
            StatusMappingEntryType entry = factory.createStatusMappingEntryType();
            entry.setAcproStatusKey(acproKey);
            entry.setJiraStatusId(jiraId);
            list.add(entry);
        }
        root.setStatusMapping(type);
    }

    private void buildResolutionMapping(Settings settings, JiraAcproSettingsType root, ObjectFactory factory) {
        Map<String, String> mapping = settings.getResolutionMapping();
        ResolutionMappingType type = factory.createResolutionMappingType();
        List<ResolutionMappingEntryType> list = type.getResolutionMappingEntry();
        for (String acproKey: mapping.keySet()) {
            String jiraId = mapping.get(acproKey);
            ResolutionMappingEntryType entry = factory.createResolutionMappingEntryType();
            entry.setAcproStatusKey(acproKey);
            entry.setJiraResolutionId(jiraId);
            list.add(entry);
        }
        root.setResolutionMapping(type);
    }

    private void buildProjectVersionMapping(Settings settings, JiraAcproSettingsType root, ObjectFactory factory) {
        Map<String, Long> mapping = settings.getProjectVersionMapping();
        ProjectVersionMappingType type = factory.createProjectVersionMappingType();
        List<ProjectVersionMappingEntryType> list = type.getProjectVersionMappingEntry();
        for (String acproName: mapping.keySet()) {
            Long jiraId = mapping.get(acproName);
            ProjectVersionMappingEntryType entry = factory.createProjectVersionMappingEntryType();
            entry.setAcproProductVersion(acproName);
            entry.setJiraProjectId(jiraId);
            list.add(entry);
        }
        root.setProjectVersionMapping(type);
    }

    private String marshal(JiraAcproSettingsType root) {
        try {
            JAXBContext context = JAXBContext.newInstance(Constants.XML_SETTINGS_GENERATED_PACKAGE);
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);            
            StringWriter writer = new StringWriter();
            marshaller.marshal(root, writer);
            String query = writer.toString();
            return query;
        } catch (JAXBException e) {
            //must not occur
            log.error("Error", e);
            throw new RuntimeException(e);
        }
    }

    public JiraAcproSettingsType unmarshalResponse(File file) throws AcproSettingsXmlException, IOException {
        //TODO validate xml
        try {
            JAXBContext context = JAXBContext.newInstance(Constants.XML_SETTINGS_GENERATED_PACKAGE);
            Unmarshaller u = context.createUnmarshaller();
            Reader reader = new FileReader(file);
            Object obj = u.unmarshal(reader);
            reader.close();
            if (!(obj instanceof JiraAcproSettingsType)) {
                throw new AcproSettingsXmlException("Xml file is not valid");
            }
            JiraAcproSettingsType retValue = (JiraAcproSettingsType) obj;
            return retValue;
        } catch (JAXBException e) {
            log.error("Error", e);
            throw new AcproSettingsXmlException("Xml file is not valid", e);
        }
    }

    public String findUserMapping(JiraAcproSettingsType loadedSettings, String acproUserKey) {
        if (loadedSettings == null) {
            return null;
        }
        UserMappingType type = loadedSettings.getUserMapping();
        if (type == null) {
            return null;
        }
        List<UserMappingEntryType> list = type.getUserMappingEntry();
        for (UserMappingEntryType entry: list) {
            if (acproUserKey.equals(entry.getAcproUserKey())) {
                return entry.getJiraUserName();
            }
        }
        return null;
    }

    public String findNotifyEmailMapping(JiraAcproSettingsType loadedSettings, String email) {
        if (loadedSettings == null) {
            return null;
        }
        NotifyEmailMappingType type = loadedSettings.getNotifyEmailMapping();
        if (type == null) {
            return null;
        }
        List<NotifyEmailMappingEntryType> list = type.getNotifyEmailMappingEntry();
        for (NotifyEmailMappingEntryType entry: list) {
            if (email.equals(entry.getEmail())) {
                return entry.getJiraUserName();
            }
        }
        return null;
    }

    public String findIssueTypeMapping(JiraAcproSettingsType loadedSettings, String acproCategoryKey) {
        if (loadedSettings == null) {
            return null;
        }
        IssueTypeMappingType type = loadedSettings.getIssueTypeMapping();
        if (type == null) {
            return null;
        }
        List<IssueTypeMappingEntryType> list = type.getIssueTypeMappingEntry();
        for (IssueTypeMappingEntryType entry: list) {
            if (acproCategoryKey.equals(entry.getAcproCategoryKey())) {
                return entry.getJiraIssueTypeId();
            }
        }
        return null;
    }

    public String findIssueTypeCategoryIsNotSetMapping(JiraAcproSettingsType loadedSettings) {
        if (loadedSettings == null) {
            return null;
        }
        IssueTypeMappingType type = loadedSettings.getIssueTypeMapping();
        if (type == null) {
            return null;
        }
        CategoryIsNotSetType categoryIsNotSet = type.getCategoryIsNotSet();
        if (categoryIsNotSet == null) {
            return null;
        }
        String jiraIssueTypeId = categoryIsNotSet.getJiraIssueTypeId();
        return jiraIssueTypeId;
    }

    public String findPriorityMapping(JiraAcproSettingsType loadedSettings, String acproUrgencyKey) {
        if (loadedSettings == null) {
            return null;
        }
        PriorityMappingType type = loadedSettings.getPriorityMapping();
        if (type == null) {
            return null;
        }
        List<PriorityMappingEntryType> list = type.getPriorityMappingEntry();
        for (PriorityMappingEntryType entry: list) {
            if (acproUrgencyKey.equals(entry.getAcproUrgencyKey())) {
                return entry.getJiraPriorityId();
            }
        }
        return null;
    }

    public String findStatusMapping(JiraAcproSettingsType loadedSettings, String acproStatusKey) {
        if (loadedSettings == null) {
            return null;
        }
        StatusMappingType type = loadedSettings.getStatusMapping();
        if (type == null) {
            return null;
        }
        List<StatusMappingEntryType> list = type.getStatusMappingEntry();
        for (StatusMappingEntryType entry: list) {
            if (acproStatusKey.equals(entry.getAcproStatusKey())) {
                return entry.getJiraStatusId();
            }
        }
        return null;
    }

    public String findResolutionMapping(JiraAcproSettingsType loadedSettings, String acproStatusKey) {
        if (loadedSettings == null) {
            return null;
        }
        ResolutionMappingType type = loadedSettings.getResolutionMapping();
        if (type == null) {
            return null;
        }
        List<ResolutionMappingEntryType> list = type.getResolutionMappingEntry();
        for (ResolutionMappingEntryType entry: list) {
            if (acproStatusKey.equals(entry.getAcproStatusKey())) {
                return entry.getJiraResolutionId();
            }
        }
        return null;
    }

    public Long findProjectVersionMapping(JiraAcproSettingsType loadedSettings, String acproProductVersionName) {
        if (loadedSettings == null) {
            return null;
        }
        ProjectVersionMappingType type = loadedSettings.getProjectVersionMapping();
        if (type == null) {
            return null;
        }
        List<ProjectVersionMappingEntryType> list = type.getProjectVersionMappingEntry();
        for (ProjectVersionMappingEntryType entry: list) {
            if (acproProductVersionName.equals(entry.getAcproProductVersion())) {
                return entry.getJiraProjectId();
            }
        }
        return null;
    }

}
